package com.example.netcamerastreamdemo.flvplayer;

import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.provider.MediaStore;
import android.util.Log;
import android.view.Surface;

import com.example.netcamerastreamdemo.flvplayer.h264.H264SPSPaser;
import com.example.netcamerastreamdemo.flvplayer.h264.SpsFrame;
import com.example.netcamerastreamdemo.flvplayer.network.HttpNet;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import javax.sql.DataSource;

import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
import static com.example.netcamerastreamdemo.flvplayer.network.HttpNet.toHexString1;

public class FlvStreamPlayer {
    private static final String TAG = "FlvStreamPlayer";

    private Decode flvDecode;
    private Surface mSurface;
    private boolean isStar;
    private ByteArrayOutputStream baos;
    //缺省长度
    private int defaultLen;
    private byte[] linshi;
    long time;
    long genTime;
    private Context mContext;
    File file;
    FileInputStream fileInputStream;

    public FlvStreamPlayer(Context context, Surface surface) {
        this.mContext = context;
        this.mSurface = surface;
    }

    private void initDecode(int width, int height) throws IOException {
        Log.i(TAG, "---width===" + width);
        Log.i(TAG, "---height===" + height);
        if (flvDecode == null) {
            flvDecode = new Decode(mSurface, width, height);
        }
    }

    public void play(){
//        new ReadLocFlvFileThread().start();
        new ExtractorThread().start();
    }

    class ReadLocFlvFileThread extends Thread{
        @Override
        public void run() {
            String path = mContext.getExternalFilesDir(null)+ "/MV/flvtest.flv";
//            String path = mContext.getExternalFilesDir(null)+ "/1623288807379.h264";
//            String path = mContext.getExternalFilesDir(null)+ "/record.mp4";
            try {
                file = new File(path);
                Log.i(TAG, "---file===" + file.length());
                fileInputStream = new FileInputStream(file);

                byte[] b = new byte[1024];
                int len = 0;
                while ((len = fileInputStream.read(b)) != -1){
                    Log.i(TAG, "---len===" + len);
                    split(Arrays.copyOfRange(b, 0, len));
                    Thread.sleep(50);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class ExtractorThread extends Thread{
        private MediaCodec decoder;

        @Override
        public void run() {
            try {

                String path = mContext.getExternalFilesDir(null)+ "/MV/flvtest.flv";
                MediaExtractor mediaExtractor = new MediaExtractor();
                mediaExtractor.setDataSource(path);
                int trackIndex = mediaExtractor.getTrackCount();
                Log.i(TAG, "---trackIndex===" + trackIndex);
                MediaFormat media0Format = mediaExtractor.getTrackFormat(0);
                Log.i(TAG, "---track0===" + media0Format.getString(MediaFormat.KEY_MIME));

                decoder = MediaCodec.createDecoderByType(MIMETYPE_VIDEO_AVC);
                decoder.configure(media0Format, mSurface, null, 0);
                decoder.start();

                MediaFormat media1Format = mediaExtractor.getTrackFormat(1);
                Log.i(TAG, "---track1===" + media1Format.getString(MediaFormat.KEY_MIME));

                mediaExtractor.selectTrack(0);
                ByteBuffer byteBuffer0 = ByteBuffer.allocate(500 * 1024);
                while (mediaExtractor.readSampleData(byteBuffer0, 0) > 0){
                    Log.i(TAG, "---byteBuffer0.array().length===" + byteBuffer0.array().length);
                    byte[] frameByte = byteBuffer0.array();
                    int inputIndex = decoder.dequeueInputBuffer(10000);
                    if (inputIndex >= 0){
                        ByteBuffer byteBuffer = decoder.getInputBuffer(inputIndex);
                        byteBuffer.put(frameByte);
                        decoder.queueInputBuffer(inputIndex, 0, frameByte.length, 0, 0);
                    }
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    int outputIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
                    if (outputIndex >= 0){
                        decoder.releaseOutputBuffer(outputIndex, true);
                    }
//                    split(Arrays.copyOfRange(byteBuffer0.array(), 0, byteBuffer0.array().length));
                    mediaExtractor.advance();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }



    private synchronized void split(byte[] data) {
//        if (data[0] == 8)
//            Log.e("aanalysis11", defaultLen + "   " + toHexString1(data, 100));
//        else if (data[0] == 9) {
//            Log.e("aanalysis22", defaultLen + "   " + toHexString1(data, 100));
//        }

        if (defaultLen > 100000) {
            isStar = false;

            return;
        }
        if (baos == null) {
            baos = new ByteArrayOutputStream();
        }
        try {
            if (defaultLen == -10 && linshi != null) {
                defaultLen = 0;
                byte[] data1 = data;
                data = new byte[data1.length + linshi.length];
                System.arraycopy(linshi, 0, data, 0, linshi.length);
                System.arraycopy(data1, 0, data, linshi.length, data1.length);
                linshi = null;
            }


            if (defaultLen != 0) {
                if (defaultLen >= 0) {
                    if (data.length >= defaultLen) {
                        int len = defaultLen;
                        baos.write(data, 0, defaultLen);
                        baos.flush();
                        defaultLen = 0;
                        analysis(baos.toByteArray());
                        baos.reset();
                        //完整帧结束
//                        if (len != data.length)
//                            split(Arrays.copyOfRange(data, len, data.length));
                        if (len < data.length - 4) {
                            defaultLen = 0;
                            split(Arrays.copyOfRange(data, len + 4, data.length));

                        } else {
                            defaultLen = data.length - len - 4;
//                            defaultLen = len - data.length - 4;
                        }


                        return;
                    } else {
                        baos.write(data, 0, data.length);
                        baos.flush();
                        defaultLen = defaultLen - data.length;

                    }
                } else {
                    if (Math.abs(defaultLen) >= data.length) {
                        defaultLen += data.length;
                    } else {
                        int leng = defaultLen;
                        defaultLen = 0;
                        split(Arrays.copyOfRange(data, Math.abs(leng), data.length));
                    }
                    return;
                }
            } else {
                if (data.length < 4) {
                    linshi = data;
                    defaultLen = -10;
                    return;
                }
                if (data[0] == 70 && data[1] == 76 && data[2] == 86) {
                    split(Arrays.copyOfRange(data, 13, data.length));
                    return;
                }
//                if (data[0] == 8 | data[0] == 9) {
//
//                } else {
//                    Log.e("yanzheng", toHexString1(data, 100));
//
//                }

                int len = byte3ToInteger(Arrays.copyOfRange(data, 1, 4)) + 11;
                if (data.length >= len) {
                    baos.write(data, 0, len);
                    baos.flush();
                    analysis(baos.toByteArray());
                    baos.reset();
                    //完整帧结束
                    if (data.length - len - 4 > 0) {
                        split(Arrays.copyOfRange(data, len + 4, data.length));
                    } else {
                        defaultLen = data.length - len - 4;
                    }
                } else {
                    baos.write(data, 0, data.length);
                    baos.flush();
                    defaultLen = len - data.length;

                }
                return;
            }
        } catch (Exception e) {
            Log.e("shipinshujuyic ", e.getMessage());
        }
    }

    private void analysis(byte[] data) {

        Log.e("analysisjk", toHexString1(data, 50));
        long time = byte4TimeToInteger(Arrays.copyOfRange(data, 4, 8));

        if (this.time == 0) {
            this.time = time;
        }
        if (genTime == 0) {
            genTime = System.currentTimeMillis();
        }

        long timekk = System.currentTimeMillis();
        if (data[0] == 9) {
            if (data[12] == 0) {
                //首帧
                if (data[11] == 23) {
                    int spsLen = byte2ToInteger(Arrays.copyOfRange(data, 22, 24));

                    byte[] sps = new byte[spsLen + 4];
                    sps[3] = 1;
                    System.arraycopy(data, 24, sps, 4, spsLen);
                    int ppsLen = byte2ToInteger(Arrays.copyOfRange(data, 25 + spsLen, 27 + spsLen));

                    H264SPSPaser h264SPSPaser = new H264SPSPaser();
                    SpsFrame spsFrame = new SpsFrame().getSpsFrame(Arrays.copyOfRange(data, 24, 24 + spsLen));
                    int width = h264SPSPaser.getWidth(spsFrame);
                    int height = h264SPSPaser.getHeight(spsFrame);

//                    Log.e("widthheight",spsFrame.toString());
                    try {
                        initDecode(width, height);
                    } catch (IOException e) {

                    }
                    byte[] pps = new byte[ppsLen + 4];
                    pps[3] = 1;
                    System.arraycopy(data, 27 + spsLen, pps, 4, ppsLen);
//                    flvDecode.onVideoFrame(sps, time,0 ,sps.length);
//                    flvDecode.onVideoFrame(pps, time,0, pps.length);
                    flvDecode.putVideoData(sps, time);
                    flvDecode.putVideoData(pps, time);
                }

            } else {
                //次帧
//                if(data[11] == 39){
//                    // P帧
//                }else if(data[11] == 23){
//                    //关键帧
//                }
                int naluLen = byte4ToInteger(Arrays.copyOfRange(data, 16, 20));
                byte[] body = new byte[naluLen + 4];
                body[3] = 1;
                System.arraycopy(data, 20, body, 4, naluLen);
//                flvDecode.onVideoFrame(body, time,0, body.length);
                flvDecode.putVideoData(body, time);
            }
        } else if (data[0] == 8) {
//            音频信息在data[11]里
            if (data[12] == 0) {
                //首帧
                flvDecode.putAudioData(Arrays.copyOfRange(data, 13, data.length), time);

            } else {
                //次帧
                flvDecode.putAudioData(Arrays.copyOfRange(data, 13, data.length), time);
            }
        }
    }

    public static int byte4TimeToInteger(byte[] value) {
        return ((value[3] & 0xff) << 24) + ((value[0] & 0xff) << 16) + ((value[1] & 0xff) << 8) + (value[2] & 0xff);
    }

    public static int byte3ToInteger(byte[] value) {
        return ((value[0] & 0xff) << 16) + ((value[1] & 0xff) << 8) + (value[2] & 0xff);
    }

    public static int byte4ToInteger(byte[] value) {
        return ((value[0] & 0xff) << 24) + ((value[1] & 0xff) << 16) + ((value[2] & 0xff) << 8) + (value[3] & 0xff);
    }

    public static int byte2ToInteger(byte[] value) {
        return ((value[0] & 0xff) << 8) + (value[1] & 0xff);
    }
}
